from pyparsing import *

import urllib
from urllib2 import *
import string as str

# Hodgkin A.L., Huxley A.F., and Katz B. (1952) Measurement of current-voltage relations in the membrane of the giant axon of Loligo. J. Physiol. 115(4):424-48

Lit = Literal
Opt = Optional
Comma = Lit(',')
Space = Lit(' ')
AND = Regex('[aA][nN][dD]')
EtAl = (Lit(' et. al.')|Lit(' et al.'))
Initials = Forward()
Initials << (Word(alphas,max=1) + Opt(Lit('.')) + Opt(Opt(Space) + Initials).leaveWhitespace())
AuthorNoComma = Word(alphas) + Opt(Space + Initials).leaveWhitespace()
AuthorComma = Word(alphas) + Optional(Comma + Optional(Space + Initials)).leaveWhitespace()
Authors1 = Forward() # no comma within author name
Authors1 << ( AuthorNoComma +
    ( Optional((AND | (Lit(';') | Optional(Comma)) + Optional(AND)) + Space + Authors1)
    | Optional(EtAl))).leaveWhitespace()
Authors2 = Forward() # no comma between author names (maybe within)
Authors2 << ( AuthorComma + 
    ( (( AND | Lit(';') + Optional(AND)) + Authors2)
    | Optional(EtAl))).leaveWhitespace()
Authors = Combine( Or([Authors1, Authors2]) ).setResultsName('Authors')

JustYear = Regex(u'[12]\d+').setResultsName('Year')
YearParen = Lit('(') + JustYear + Lit(')')
Year = ( JustYear | YearParen ) + Optional(Lit('.'))

Title = Combine(Regex(u'[-a-zA-Z:, ]+') + ( Lit('.'))).setResultsName('Title')
Journal = Regex(u'[a-zA-Z. ]+[a-zA-Z.]').setResultsName('Journal').setName('Journal')
Pages = ( Word(nums).setResultsName('FirstPage') + 
    Optional( Lit(u'-') + Word(nums).setResultsName('LastPage')) )
Volume = (Word(nums).setResultsName('Volume') + 
    Optional( Lit('(') + Word(nums).setResultsName('Issue') + Lit(')')))
VolPgDelim = ( Lit(u':') | Lit(u',') )

Reference = (
    Authors + Year + Title + Journal + Optional(Volume + VolPgDelim + Pages) | 
    Authors + Title + Journal + Volume + VolPgDelim + Pages + YearParen
)

class CiteParser:
    def __init__(self):
        self.throwErrors = False
    def parse( self, text ):
        try:
            parsed = Reference.parseString(text)
            return(parsed)
        except ParseException:
            if self.throwErrors:
                raise
            return(None)
